package com.spark.common.helper

import android.os.Parcelable
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter
import android.support.v4.app.FragmentStatePagerAdapter
import android.support.v4.view.PagerAdapter
import android.view.View
import android.view.ViewGroup

interface MutablePagerFragmentController {

    fun findByIndex(index: Int): Fragment?

    fun indexOf(fragment: Fragment): Int

    fun add(fragment: Fragment)

    fun adds(fragments: MutableList<Fragment>)

    fun insert(index: Int, fragment: Fragment)

    fun replace(index: Int, fragment: Fragment)

    fun pop()

    fun remove(index: Int)

    fun remove(fragment: Fragment)

    fun clear()
}

/**
 * <><><> MutableStatePagerAdapter <><><>
 */
open class MutableStatePagerAdapter(var fm: FragmentManager) : FragmentStatePagerAdapter(fm), MutablePagerFragmentController {
    val pageList = mutableListOf<Page>()

    override fun getItem(position: Int): Fragment {
        return pageList[position].fragment
    }

    override fun getCount(): Int {
        return pageList.size
    }

    override fun getItemPosition(`object`: Any): Int {
        pageList.forEachIndexed { i, it ->
            if (`object` == it.fragment) {
                return if (it.position >= 0 && it.fragment.isAdded && !it.fragment.isDetached) {
                    if (it.position == i) PagerAdapter.POSITION_UNCHANGED
                    else i
                } else {
                    it.position = i
                    PagerAdapter.POSITION_NONE
                }
            }
        }
        return PagerAdapter.POSITION_NONE
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        if (position > count) {
            fm.beginTransaction()?.apply {
                remove(`object` as Fragment)
                commit()
            }
        }
        super.destroyItem(container, position, `object`)
    }

    override fun isViewFromObject(view: View, `object`: Any): Boolean {
        if (`object` != null && view == (`object` as Fragment).view) return true
        return super.isViewFromObject(view, `object`)
    }

    override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
        super.setPrimaryItem(container, position, `object`)
    }

    override fun findByIndex(index: Int): Fragment? {
        if (index < 0 || index > pageList.size)
            return null
        return pageList[index].fragment
    }

    override fun indexOf(fragment: Fragment): Int {
        return pageList.indexOf(Page(fragment))
    }

    override fun add(fragment: Fragment) {
        if (pageList.contains(Page(fragment)))
            return
        pageList.add(Page(fragment))
        notifyDataSetChanged()
    }

    override fun adds(fragments: MutableList<Fragment>) {
        if (fragments.isEmpty()) return
        fragments.forEach {
            val element = Page(it)
            if (!pageList.contains(element))
                pageList.add(element)
        }
        notifyDataSetChanged()
    }

    override fun insert(index: Int, fragment: Fragment) {
        if (pageList.contains(Page(fragment))) return
        if (index < 0 || index >= pageList.size) return
        pageList.add(index, Page(fragment))
        for (i in index until pageList.size) {
            val page = pageList[i]
            if (page.fragment.isAdded)
                page.position = -1
        }
        notifyDataSetChanged()
    }

    override fun replace(index: Int, fragment: Fragment) {
        if (index < 0 || index >= pageList.size) return
        val origin = pageList[index].fragment
        fm.beginTransaction().apply {
            remove(origin)
            commitNow()
        }
        pageList[index] = Page(fragment)
        notifyDataSetChanged()
    }

    override fun pop() {
        if (pageList.size == 0) return
        pageList.removeAt(pageList.size - 1)
        notifyDataSetChanged()
    }

    override fun remove(index: Int) {
        if (index < 0 || index >= pageList.size) return
        for (i in index until pageList.size) {
            val page = pageList[i]
            if (page.fragment.isAdded)
                page.position = -1
        }
        pageList.removeAt(index)
        notifyDataSetChanged()
    }

    override fun remove(fragment: Fragment) {
        if (!pageList.contains(Page(fragment))) return
        val index = pageList.indexOf(Page(fragment))
        if (index >= 0) {
            for (i in index until pageList.size) {
                val page = pageList[i]
                if (page.fragment.isAdded)
                    page.position = -1
            }
            pageList.remove(Page(fragment))
            notifyDataSetChanged()
        }
    }

    override fun clear() {
        pageList.clear()
        notifyDataSetChanged()
    }


    class Page(var fragment: Fragment) {
        var position = -1
        override fun equals(other: Any?): Boolean {
            if (other is Page && other.fragment == fragment) return true
            return super.equals(other)
        }
    }


}

/**
 * <><><> MutablePagerAdapter <><><>
 */
open class MutablePagerAdapter(var fm: FragmentManager) : FragmentPagerAdapter(fm), MutablePagerFragmentController {
     val pageList = mutableListOf<Page>()

    override fun getItem(position: Int): Fragment {
        return pageList[position].fragment
    }

    override fun getCount(): Int {
        return pageList.size
    }

    override fun getItemId(position: Int): Long {
        return pageList[position].id
    }

    override fun getItemPosition(`object`: Any): Int {
        pageList.forEachIndexed { i, it ->
            if (`object` == it.fragment) {
                return if (it.position >= 0 && it.fragment.isAdded && !it.fragment.isDetached) {
                    if (it.position == i) {
                        PagerAdapter.POSITION_UNCHANGED
                    } else i
                } else {
                    it.position = i
                    PagerAdapter.POSITION_NONE
                }
            }
        }
        return PagerAdapter.POSITION_NONE
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        if (position >= count) {
            fm.beginTransaction()?.apply {
                remove(`object` as Fragment)
                commit()
            }
        }
        super.destroyItem(container, position, `object`)
    }

    override fun isViewFromObject(view: View, `object`: Any): Boolean {
        if (view == (`object` as Fragment).view) return true
        return super.isViewFromObject(view, `object`)
    }

    override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
        super.setPrimaryItem(container, position, `object`)
    }

    override fun findByIndex(index: Int): Fragment? {
        if (index < 0 || index >= pageList.size) return null
        return pageList[index].fragment
    }

    override fun indexOf(fragment: Fragment): Int {
        return pageList.indexOf(Page(fragment))
    }

    override fun add(fragment: Fragment) {
        if (pageList.contains(Page(fragment))) return
        pageList.add(Page(fragment))
        notifyDataSetChanged()
    }

    override fun adds(fragments: MutableList<Fragment>) {
        if (fragments.isEmpty()) return
        fragments.forEach {
            val element = Page(it)
            if (!pageList.contains(element))
                pageList.add(element)
        }
        notifyDataSetChanged()
    }

    override fun insert(index: Int, fragment: Fragment) {
        if (pageList.contains(Page(fragment))) return
        if (index < 0 || index >= pageList.size) return
        pageList.add(index, Page(fragment))
        for (i in index until pageList.size) {
            val page = pageList[i]
            if (page.fragment.isAdded)
                pageList[i].position = -1
        }
        notifyDataSetChanged()
    }

    override fun replace(index: Int, fragment: Fragment) {
        if (index < 0 || index >= pageList.size) return
        val current = pageList[index].fragment
        fm.beginTransaction()?.apply {
            remove(current)
            commit()
        }
        pageList[index] = Page(fragment)
        notifyDataSetChanged()
    }

    override fun pop() {
        if (pageList.size == 0) return
        pageList.removeAt(pageList.size - 1)
        notifyDataSetChanged()
    }

    override fun remove(index: Int) {
        if (index < 0 || index >= pageList.size) return
        val ft = fm.beginTransaction()
        for (i in index until pageList.size) {
            val page = pageList[i]
            if (page.fragment.isAdded) {
                page.position = -1
                ft.detach(page.fragment)
            }
        }
        ft.commit()
        pageList.removeAt(index)
        notifyDataSetChanged()
    }

    override fun remove(fragment: Fragment) {
        val element = Page(fragment)
        if (!pageList.contains(element)) return
        val index = pageList.indexOf(element)
        if (index >= 0) {
            val ft = fm.beginTransaction()
            for (i in index until pageList.size) {
                val page = pageList[i]
                if (page.fragment.isAdded) {
                    page.position = -1
                    ft.detach(page.fragment)
                }
            }
            ft.commit()
            pageList.remove(element)
            notifyDataSetChanged()
        }
    }

    override fun clear() {
        val ft = fm.beginTransaction()
        pageList.forEach {
            ft.remove(it.fragment)
        }
        ft.commit()
        pageList.clear()
        notifyDataSetChanged()
    }

    class Page(var fragment: Fragment) {
        var position = -1
        var id = 0L

        companion object {
            private var serialNum = 0L
            fun resetSerialNum() {
                serialNum = 0
            }
        }

        init {
            id = serialNum++
        }

        override fun equals(other: Any?): Boolean {
            if (other is Page && other.fragment == fragment) return true
            return super.equals(other)
        }

    }
}